CSS ë·° ížëì§ì ì ì ëë©ìŽì ì í ì°êŽì ëí ì¬ìžµ ë¶ì. 'view-transition-class' ë° êž°í CSS ìì±ì ì¬ì©íì¬ ì êµí ì í íšê³Œë¥Œ ì ìŽíë ë°©ë²ì í구í©ëë€.
CSS ë·° ížëì§ì ì í ë§€ì¹: ì ëë©ìŽì ì í ì°êŽ ë§ì€í°íêž°
CSS ë·° ížëì§ì ì ì¹ ì í늬ìŒìŽì ì ì¬ë¬ ìí ê°ì ë¶ëëœê³ ìê°ì ìŒë¡ ë§€ë ¥ì ìž ì íì ë§ëë ê°ë ¥íê³ ì°ìí ë°©ë²ì ì ê³µí©ëë€. ë·° ížëì§ì ì íšê³Œì ìŒë¡ ì¬ì©íë íµì¬ 잡멎ì ì ëë©ìŽì ì í ì°êŽ(animation type association)ì ìŽíŽíë ê²ì ëë€. ìŽë¥Œ íµíŽ ì í ì€ ë€ìí ììì ì ì©ëë í¹ì ì ëë©ìŽì ì ì ìŽí ì ììµëë€. ìŽ êžììë ì ëë©ìŽì ì í ì°êŽì ë³µì¡ì±ì ê¹ìŽ íê³ ë€ìŽ, ë©ì§ ì¬ì©ì 겜íì ìíŽ ìŽë¥Œ íì©íë ë°©ë²ì ëí ì€ì©ì ìž ìì ì ì§ì¹šì ì ê³µí©ëë€.
ë·° ížëì§ì ì Ʞ볞 ìŽíŽíêž°
ì ëë©ìŽì ì í ì°êŽì ëíŽ ìì볎Ʞ ì ì CSS ë·° ížëì§ì ì Ʞ볞 ì¬íì ê°ëµíê² ìŽíŽë³Žê² ìµëë€. ë·° ížëì§ì ì DOM ìí ê°ì ë³ê²œ ì¬íì ì ëë©ìŽì ìŒë¡ ë§ëë íì€íë ë©ì»€ëìŠì ì ê³µí©ëë€. ìí ë³ê²œ(ì: ì±êž íìŽì§ ì í늬ìŒìŽì ìì íìŽì§ ê° ìŽë ëë 컎í¬ëíž ëŽ ìœí ìž ì ë°ìŽíž)ìŽ ë°ìí멎 ë·° ížëì§ì ì ë³ê²œ ì í ììì ìí륌 캡ì²í©ëë€. ìŽë ê² ìº¡ì²ë ìíë ì ëë©ìŽì ì íì ë§ëë ë° ì¬ì©ë©ëë€.
íµì¬ ë©ì»€ëìŠì document.startViewTransition() íšìì ìíŽ ììëë©°, ìŽ íšìë DOMì ìë¡ìŽ ìíë¡ ì
ë°ìŽížíë ìœë°±ì ìžìë¡ ë°ìµëë€.
ìì :
document.startViewTransition(() => {
// DOMì ìë¡ìŽ ìíë¡ ì
ë°ìŽíž
updateTheDOM();
});
view-transition-nameì í
view-transition-name CSS ìì±ì ë·° ížëì§ì
ì ì°žì¬íŽìŒ í ìì륌 ìë³íë êž°ìŽì
ëë€. ëìŒí view-transition-nameì ê°ì§ ììë€ì ì¬ë¬ ìíì ê±žì³ ë
ŒëЬì ìŒë¡ ì°ê²°ë ê²ìŒë¡ ê°ì£Œë©ëë€. ê·žë¬ë©Ž ëžëŒì°ì ë ìŽë¬í ììë€ì 'ìŽì (old)' ë° 'ìë¡ìŽ(new)' ìí륌 ëíëŽë ìì¬ ìì륌 ìëìŒë¡ ìì±íì¬ ì ëë©ìŽì
ì ì ì©í ì ìê² íŽì€ëë€.
ìì :
.card {
view-transition-name: card-element;
}
ìŽ ìì ìì 'card' íŽëì€ë¥Œ ê°ì§ 몚ë ììë DOM ì
ë°ìŽíž ì íì ìíê° ìº¡ì²ëë©°, view-transition-nameìŽ ì
ë°ìŽíž ê°ì ìŒêŽëê² ì ì§ëë€ë©Ž ì íì ì°žì¬íê² ë©ëë€.
ì ëë©ìŽì
ì í ì°êŽ: view-transition-class ìê°
ì£Œë¡ view-transition-class CSS ìì±ì íµíŽ êµ¬íëë ì ëë©ìŽì
ì í ì°êŽì ë·° ížëì§ì
ì€ì ì ì©ëë ì ëë©ìŽì
ì ì¬ì©ì ì ìíë íµì¬ì
ëë€. ìŽë¥Œ íµíŽ ì í ëŽìì ììì ìí ìŽë ì íì ë°ëŒ ë€ìí ì ëë©ìŽì
ì ì§ì í ì ììµëë€. ì íì ì¬ë¬ ë¶ë¶ì ì ëë©ìŽì
"ìí "ì í ë¹íë ê²ìŽëŒê³ ìê°í멎 ë©ëë€.
view-transition-class ìì±ì ë€ë¥ž CSS ìì±ì²ëŒ ììì í ë¹ë©ëë€. ê°ì 묞ììŽìŽë©°, ìŽ ë¬žììŽì CSSìì ì ì í ::view-transition-* ìì¬ ìì륌 ì ííë ë° ì¬ì©ë©ëë€.
ì§ì í íì view-transition-class륌 ::view-transition-group, ::view-transition-image-pair, ::view-transition-new, ê·žëŠ¬ê³ ::view-transition-old ìì¬ ììì ê²°í©í ë ë°íë©ëë€.
ìì¬ ìì ìŽíŽíêž°
::view-transition-group(view-transition-name): ì§ì ëview-transition-nameì ê°ì§ ììì ìŽì ìíì ìë¡ìŽ ìí륌 몚ë í¬íšíë 귞룹ì ëíë ëë€. ìŽê²ì ì íì ìí ìµìì 컚í ìŽëì ëë€.::view-transition-image-pair(view-transition-name): ë·° ížëì§ì ì ìŽë¯žì§ê° í¬íšë ë ìŽì ìŽë¯žì§ì ì ìŽë¯žì§ë¥Œ 몚ë ê°ìëë€. ìŽë¥Œ íµíŽ ìŽì ìŽë¯žì§ì ì ìŽë¯žì§ ê°ì ëêž°íë ì ëë©ìŽì ìŽ ê°ë¥í©ëë€.::view-transition-new(view-transition-name): ììì *ìë¡ìŽ* ìí륌 ëíë ëë€.::view-transition-old(view-transition-name): ììì *ìŽì * ìí륌 ëíë ëë€.
ì ëë©ìŽì ì í ì°êŽì ì€ì ìì
ì ëë©ìŽì ì í ì°êŽìŽ ì€ì ë¡ ìŽë»ê² ìëíëì§ ì€ëª íêž° ìíŽ ëª ê°ì§ ì€ì ìì 륌 ìŽíŽë³Žê² ìµëë€.
ìì 1: ìë¡ìŽ ìœí ìž íìŽë ìž
ììŽí
목ë¡ìŽ ìê³ , ìë¡ìŽ ììŽí
ìŽ ì¶ê°ë ë íìŽë ìž íšê³Œë¥Œ ì£Œê³ ì¶ë€ê³ ê°ì íŽ ëŽ
ìë€. view-transition-classì ::view-transition-new륌 ì¬ì©íì¬ ìŽë¥Œ 구íí ì ììµëë€.
HTML:
<ul id="item-list">
<li class="item" style="view-transition-name: item-1;">Item 1</li>
<li class="item" style="view-transition-name: item-2;">Item 2</li>
</ul>
JavaScript (ì ììŽí ì¶ê°ì©):
function addNewItem() {
document.startViewTransition(() => {
const newItem = document.createElement('li');
newItem.classList.add('item');
newItem.style.viewTransitionName = `item-${Date.now()}`;
newItem.style.viewTransitionClass = 'new-item'; // íŽëì€ í ë¹
newItem.textContent = 'New Item';
document.getElementById('item-list').appendChild(newItem);
});
}
CSS:
::view-transition-new(item-*) {
animation: fade-in 0.5s ease-in-out;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
ìŽ ìì ììë ë·° ížëì§ì
ì ì ìë¡ìŽ ëª©ë¡ ììŽí
ì 'new-item' íŽëì€ë¥Œ í ë¹í©ëë€. ê·žë° ë€ì CSSë ::view-transition-new ìì¬ ìì륌 íê²í
íì¬(view-transition-name ì€íìŒì `item-*` ìŽëŠê³Œ ìŒì¹) íìŽë ìž ì ëë©ìŽì
ì ì ì©í©ëë€. ì¬êž°ì `new-item` íŽëì€ ì첎ë CSS ì íììì ì¬ì©ëì§ *ìëë€ë* ì ì ì ìíìžì. view-transition-class ìì±ì *ê°*ì ìŽë€ *ì€ì * ììì ì€ì íê³ ìëì§ë¥Œ ê³ ë €í ëë§ ì€ìí©ëë€.
ìì 2: ìŽì ìœí ìž ì¬ëŒìŽë ìì
ìŽì ìì 륌 êž°ë°ìŒë¡, ì ììŽí ìŽ íìŽë ìžëë ëì ìŽì ììŽí ìŽ ì¬ëŒìŽë ììëëë¡ ë§ë€ìŽ ëŽ ìë€.
JavaScript (ìŽì 곌 ëìŒ):
function addNewItem() {
document.startViewTransition(() => {
const newItem = document.createElement('li');
newItem.classList.add('item');
newItem.style.viewTransitionName = `item-${Date.now()}`;
newItem.style.viewTransitionClass = 'new-item'; // íŽëì€ í ë¹
newItem.textContent = 'New Item';
document.getElementById('item-list').appendChild(newItem);
});
}
CSS:
::view-transition-new(item-*) {
animation: fade-in 0.5s ease-in-out;
}
::view-transition-old(item-*) {
animation: slide-out 0.5s ease-in-out;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-out {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(-100%); opacity: 0; }
}
ì¬êž°ìë ::view-transition-old ìì¬ ììì ì ëë©ìŽì
ì ì¶ê°íì¬, ìŽì ììŽí
ìŽ íìŽë ììë멎ì ìŒìªœìŒë¡ ì¬ëŒìŽëëëë¡ íìµëë€. ë€ì í ë², view-transition-classë ì°ëŠ¬ê° ì¶ê°íë *ìë¡ìŽ* ìììë§ êŽë šìŽ ììŒë©°, íìŽì§ì ìŽë¯ž ìê³ ì íì ì°žì¬íë *ìŽì * ìììë ìí¥ì 믞ì¹ì§ ììµëë€.
ìì 3: ë ë³µì¡í ë€ë¹ê²ìŽì ì í
ë€ë¹ê²ìŽì
ë©ëŽê° ìë ì±êž íìŽì§ ì í늬ìŒìŽì
(SPA)ì ìê°íŽ ëŽ
ìë€. ì¬ì©ìê° ë©ëŽ í목ì íŽëŠí멎 ìœí
ìž ìììŽ ì íìŽì§ë¡ ë¶ëëœê² ì íëìŽìŒ í©ëë€. view-transition-class륌 ì¬ì©íì¬ í€ëì ìœí
ìž ììì 구ë¶íê³ ê°ê°ì ë€ë¥ž ì ëë©ìŽì
ì ì ì©í ì ììµëë€.
HTML (ê°ìí):
<header style="view-transition-name: header; view-transition-class: header-transition;">
<h1>My Website</h1>
</header>
<main style="view-transition-name: content; view-transition-class: content-transition;">
<p>Initial Content</p>
</main>
JavaScript (ê°ìí):
function navigateTo(pageContent) {
document.startViewTransition(() => {
document.querySelector('main').innerHTML = pageContent;
});
}
CSS:
::view-transition-old(header) {
animation: fade-out 0.3s ease-in-out;
}
::view-transition-new(header) {
animation: fade-in 0.3s ease-in-out;
}
::view-transition-old(content) {
animation: slide-out-left 0.5s ease-in-out;
}
::view-transition-new(content) {
animation: slide-in-right 0.5s ease-in-out;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-out-left {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
@keyframes slide-in-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
ìŽ ìë늬ì€ìì í€ëë íìŽë ìž/ììëê³ , ìœí
ìž ë ì€ë¥žìªœìì ì¬ëŒìŽë ìž, ìŒìªœìŒë¡ ì¬ëŒìŽë ììëìŽ ëì ìŽê³ ë§€ë ¥ì ìž ë€ë¹ê²ìŽì
겜íì ë§ëëë€. ìŽë header-transition ë° content-transition íŽëì€ë¥Œ ì ì©íì¬ í€ëì ìœí
ìž ììì ê°ê° ë€ë¥ž ì ëë©ìŽì
ìŒë¡ ê°ë³ì ìŒë¡ íê²í
íšìŒë¡ìš ë¬ì±íìµëë€.
ì ëë©ìŽì ì í ì°êŽ ì¬ì©ì ìí ëªšë² ì¬ë¡
ì ëë©ìŽì ì í ì°êŽì íšê³Œì ìŒë¡ íì©íë €ë©Ž ë€ì ëªšë² ì¬ë¡ë¥Œ ê³ ë €íìžì:
- ì í ê³ííêž°: ì íì 구ííêž° ì ì ìíë ì ëë©ìŽì 곌 ê·žê²ìŽ ì¬ì©ì 겜íì ìŽë»ê² í¥ììí¬ì§ ì ì€íê² ê³ííìžì. ì 볎ì íëŠì ê³ ë €íê³ ì¬ì©ì륌 ìê°ì ìŒë¡ ë³ê²œ ì¬íì íµíŽ ìëŽíë ë°©ë²ì ìê°íìžì.
- ì€ëª ì ìž íŽëì€ ìŽëŠ ì¬ì©íêž°: ì íìì ììì ìí ì ëª ííê² ëíëŽë íŽëì€ ìŽëŠì ì ííìžì (ì: 'new-item', 'old-item', 'header-transition'). ìŽë ìœë ê°ë ì±ê³Œ ì ì§ë³Žìì±ì í¥ììíµëë€.
- ì ëë©ìŽì ì ê°ê²°íê² ì ì§íêž°: ì¬ì©ì륌 ì°ë§íê² íê±°ë ì í늬ìŒìŽì ìë륌 ì íìí¬ ì ìë ì§ëì¹ê² ë³µì¡íê±°ë ꞎ ì ëë©ìŽì ì íŒíìžì. ì¬ì©ì 겜íì ë°©íŽíì§ ìê³ í¥ììí€ë ë¶ëëœê³ 믞ë¬í ì íì 목íë¡ íìžì. ì¬ëì ëì ìë°± ë°ëЬìŽë³Žë€ ꞎ ì§ì°ì 믌ê°íë¯ë¡ ì íì ë¹ ë¥Žê² ì ì§íìžì.
- ì² ì íê² í ì€ížíêž°: ë€ìí êž°êž°ì ëžëŒì°ì ìì ì íì í ì€ížíì¬ ì¬ë°ë¥Žê² ë ëë§ëê³ ìííê² ìëíëì§ íìžíìžì. í¹í 몚ë°ìŒ êž°êž°ììì ì±ë¥ì 죌ì륌 êž°ìžìŽìžì.
- ì ê·Œì± ê³ ë €íêž°: ìì§ìì 믌ê°í ì¬ì©ì륌 ìŒëì ëìžì. ì ëë©ìŽì
ì ë¹íì±ííê±°ë ê°ë륌 ì€ìŒ ì ìë ìµì
ì ì ê³µíìžì.
prefers-reduced-motion믞ëìŽ ì¿ŒëŠ¬ë¥Œ ì¬ì©íì¬ ì¬ì©ìê° ìŽì 첎ì ì€ì ìì ìì§ì ê°ì륌 ìì²íëì§ ê°ì§í ì ììµëë€. - CSS ìºì€ìŒìŽë íšê³Œì ìŒë¡ ì¬ì©íêž°: CSS ìºì€ìŒìŽë륌 íì©íì¬ ì ëë©ìŽì ì êŽëЬíìžì. ê³µíµ ì ëë©ìŽì ìì±ì Ʞ볞 íŽëì€ì ì ìí ë€ì, ë€ë¥ž ë·° ížëì§ì ìíì ëíŽ í¹ì ìì±ì ì¬ì ìíìžì.
ê³ êž êž°ì ë° ê³ ë € ì¬í
ëì íŽëì€ í ë¹
ìì ìì ììë view-transition-name곌 view-transition-classì ìžëŒìž ì€íìŒì ì¬ì©íì§ë§, ì€ì ì í늬ìŒìŽì
ììë JavaScript륌 ì¬ì©íì¬ ìŽë¥Œ ëì ìŒë¡ êŽëЬíê³ ì¶ì ê²ì
ëë€. ìŽë¥Œ íµíŽ í¹ì ìí ë³ê²œìŽë ì¬ì©ì ìíž ìì©ì ë°ëŒ ë€ë¥ž íŽëì€ë¥Œ ì ì©í ì ììµëë€.
ìì :
function updateContent(newContent, transitionType) {
document.startViewTransition(() => {
const mainElement = document.querySelector('main');
mainElement.innerHTML = newContent;
// Ʞ졎 ì í íŽëì€ ì ê±°
mainElement.classList.remove('slide-in', 'fade-in');
// ì ì í ì í íŽëì€ ì¶ê°
if (transitionType === 'slide') {
mainElement.classList.add('slide-in');
} else if (transitionType === 'fade') {
mainElement.classList.add('fade-in');
}
});
}
ìŽ ìì ë ìíë ì í ì íì ë°ëŒ ì ëë©ìŽì ì ì ìŽíêž° ìíŽ CSS íŽëì€ë¥Œ ëì ìŒë¡ ì¶ê°íë ë°©ë²ì 볎ì¬ì€ëë€.
ë³µì¡í 컎í¬ëíž ìì
ë³µì¡í 컎í¬ëížë¥Œ ë€ë£° ëë 컎í¬ëíž ëŽì ë€ë¥ž ììì ì¬ë¬ view-transition-name ë° view-transition-class ê°ì í ë¹íŽìŒ í ì ììµëë€. ìŽë¥Œ íµíŽ ë ìžë¶íëê³ ì ìŽë ì íì ë§ë€ ì ììµëë€.
ìì :
<div style="view-transition-name: component;">
<h2 style="view-transition-name: component-title; view-transition-class: title-transition;">Component Title</h2>
<p style="view-transition-name: component-content; view-transition-class: content-transition;">Component Content</p>
</div>
ìŽ ìì ìì 컎í¬ëíž ì첎ì ì 목 ë° ìœí
ìž ìì 몚ëì view-transition-nameìŽ ììµëë€. ìŽë¥Œ íµíŽ ì 첎 컎í¬ëížë¥Œ íëì ëšìë¡ ì ëë©ìŽì
íí멎ì ì 목곌 ìœí
ìž ì ê°ë³ì ìŒë¡ í¹ì ì ëë©ìŽì
ì ì ì©í ì ììµëë€.
ë¹ëêž° ìì ì²ëЬ
ìí ì
ë°ìŽížì ë¹ëêž° ìì
(ì: APIìì ë°ìŽí° ê°ì žì€êž°)ìŽ í¬íšë 겜ì°, document.startViewTransition() ìœë°±ìŽ ë¹ëêž° ìì
ìŽ ìë£ë *íì* ì€íëëë¡ íŽìŒ í©ëë€. ìŽë íë¡ë¯žì€ë async/await륌 ì¬ì©íì¬ ë¬ì±í ì ììµëë€.
ìì :
async function updateContentAsync(newContentUrl) {
document.startViewTransition(async () => {
const response = await fetch(newContentUrl);
const newContent = await response.text();
document.querySelector('main').innerHTML = newContent;
});
}
í¬ë¡ì€ ëžëŒì°ì ížíì± ë° íŽëЬí
2024ë íë° íì¬, CSS ë·° ížëì§ì ì Chrome, Edge, Firefoxì ê°ì ìµì ëžëŒì°ì ìì ì ì§ìë©ëë€. ê·žë¬ë 구í ëžëŒì°ì ë Safariììë ì§ìì ìíŽ íŽëЬíìŽ íìí ì ììµëë€. íë¡ëì ì ë°°í¬íêž° ì ì ì¬ë¬ ëžëŒì°ì ìì ì íì í ì€ížíê³ íìí ê²œì° Open UI ìŽëì í°ëžìì ì ê³µíë ê²ê³Œ ê°ì íŽëЬí ì¬ì©ì ê³ ë €íë ê²ìŽ ì€ìí©ëë€.
CSS ë·° ížëì§ì ì êŽë²ìíê² êµ¬ííêž° ì ì caniuse.com곌 ê°ì ì¬ìŽížìì íì¬ ëžëŒì°ì ì§ì íí©ì íìžíìžì.
ë·° ížëì§ì ì 믞ë
CSS ë·° ížëì§ì ì ì¹ ì ëë©ìŽì ë° ì¬ì©ì 겜íìì ì€ìí ì§ì ì ëíë ëë€. ì¬ììŽ ë°ì íê³ ëžëŒì°ì ì§ììŽ íëëšì ë°ëŒ ìŽ êž°ì ì ëì± ì êµíê³ ì°œìì ìž ì¬ì©ì êž°ëí ì ììµëë€. ë·° ížëì§ì APIì ìë¡ìŽ êž°ë¥ê³Œ ì ë°ìŽížì 죌목íì¬ ìµì ëí¥ì íì íìžì.
ê²°ë¡
view-transition-class ìì±ì ìíŽ ìŽì§ëë ì ëë©ìŽì
ì í ì°êŽì CSS ë·° ížëì§ì
ì ë§ì€í°íë ë° ììŽ ì€ìí 잡멎ì
ëë€. íŽëì€ë¥Œ ì¬ì©íì¬ ììì ë€ë¥ž ì ëë©ìŽì
"ìí "ì í ë¹íê³ ::view-transition-* ìì¬ ììë¡ íê²í
íë ë°©ë²ì ìŽíŽíšìŒë¡ìš ì¹ ì í늬ìŒìŽì
ì ì¬ì©ì 겜íì í¥ììí€ë ë©ì§ê³ ë§€ë ¥ì ìž ì íì ë§ë€ ì ììµëë€. ì íì ì ì€íê² ê³ííê³ , ì€ëª
ì ìž íŽëì€ ìŽëŠì ì¬ì©íê³ , ì ëë©ìŽì
ì ê°ê²°íê² ì ì§íê³ , ì² ì íê² í
ì€ížíê³ , ì ê·Œì±ì ê³ ë €íë ê²ì ìì§ ë§ìžì. ìŽë¬í ìì¹ì ìŒëì ë멎 CSS ë·° ížëì§ì
ì ì ì¬ë ¥ì ìµëí ë°ííê³ ì ìžê³ ì¬ì©ì륌 ìí ì§ì ìŒë¡ ëëŒìŽ ì¹ ê²œíì ë§ë€ ì ììµëë€.
CSS ë·° ížëì§ì ì ì ì€í ì ì©ê³Œ ì ëë©ìŽì ì í ì°êŽì ëí íì€í ìŽíŽë ì¹ì¬ìŽížë ì¹ ì í늬ìŒìŽì ì ìžì§ ì±ë¥ê³Œ ì ë°ì ìž ìì±ë륌 ê·¹ì ìŒë¡ í¥ììí¬ ì ììµëë€. ìŽë ë íë³µí ì¬ì©ìì ìœí ìž ì ì 묞ì ìž ííìŒë¡ ìŽìŽì§ëë€. ë€ìí ì ëë©ìŽì ì í곌 ì í ì§ì ìê°ì ì€ííì¬ í¹ì ì구ì ë§ë ì벜í ê· íì ì°ŸìŒìžì. ìŠê±°ìŽ ìœë© ëìžì!